Abstract Classes এবং Interfaces হ্যান্ডল করা

Java Technologies - জ্যাকসন (Jackson) - Polymorphic Type Handling
180

Jackson ডিফল্টভাবে abstract classes বা interfaces সিরিয়ালাইজ এবং ডেসিরিয়ালাইজ করতে পারে না, কারণ এগুলোর ইনস্ট্যান্স তৈরি করা সম্ভব নয়। তবে, সঠিক কনফিগারেশন এবং কাস্টম পলিসি ব্যবহারের মাধ্যমে Jackson এ abstract classes এবং interfaces হ্যান্ডল করা সম্ভব।


Abstract Class এবং Interface কেন সমস্যা তৈরি করে?

  1. Abstract Class: এটি সরাসরি ইনস্ট্যান্সিয়েট করা যায় না।
  2. Interface: এটি শুধুমাত্র মেথডের সিগনেচার সংজ্ঞায়িত করে, ইনস্ট্যান্সিয়েট করা যায় না।

এগুলোর জন্য Jackson কে জানাতে হবে:

  • ডেটার আসল টাইপ (Sub-Class)।
  • কিভাবে সিরিয়ালাইজ এবং ডেসিরিয়ালাইজ করতে হবে।

সমাধানের পদ্ধতি

১. Type Information অন্তর্ভুক্ত করা

Jackson-এর @JsonTypeInfo এবং @JsonSubTypes অ্যানোটেশন ব্যবহার করে টাইপ ইনফরমেশন সংজ্ঞায়িত করা যায়।

উদাহরণ:
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

// Abstract Class
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,      // টাইপ ইনফরমেশন সংরক্ষণ
    include = JsonTypeInfo.As.PROPERTY,
    property = "type"               // JSON-এ "type" ফিল্ড যোগ হবে
)
@JsonSubTypes({
    @JsonSubTypes.Type(value = Car.class, name = "car"),
    @JsonSubTypes.Type(value = Truck.class, name = "truck")
})
abstract class Vehicle {
    public String brand;
}

// Sub-Class 1
class Car extends Vehicle {
    public int seatingCapacity;

    public Car() {}
}

// Sub-Class 2
class Truck extends Vehicle {
    public double payloadCapacity;

    public Truck() {}
}
Serialization:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class SerializationExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.INDENT_OUTPUT);

        Vehicle car = new Car();
        car.brand = "Toyota";
        ((Car) car).seatingCapacity = 5;

        String json = mapper.writeValueAsString(car);
        System.out.println("Serialized JSON:\n" + json);
    }
}
Output:
{
  "type": "car",
  "brand": "Toyota",
  "seatingCapacity": 5
}
Deserialization:
public class DeserializationExample {
    public static void main(String[] args) throws Exception {
        String json = "{ \"type\": \"truck\", \"brand\": \"Ford\", \"payloadCapacity\": 10000.0 }";

        ObjectMapper mapper = new ObjectMapper();
        Vehicle vehicle = mapper.readValue(json, Vehicle.class);

        System.out.println("Deserialized Vehicle: " + vehicle.brand + ", Type: " + vehicle.getClass().getSimpleName());
    }
}
Output:
Deserialized Vehicle: Ford, Type: Truck

২. Mix-in Annotations ব্যবহার করা

যদি আপনার কাছে Abstract Class বা Interface-এর সোর্স কোড না থাকে, তবে Mix-in Annotations ব্যবহার করে টাইপ ইনফরমেশন যোগ করা সম্ভব।

উদাহরণ:
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;

// Abstract Class
abstract class Shape {
    public String color;
}

// Sub-Class
class Circle extends Shape {
    public double radius;
}

// Mix-in Annotation
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "type"
)
@JsonSubTypes({
    @JsonSubTypes.Type(value = Circle.class, name = "circle")
})
abstract class ShapeMixIn {}
কনফিগার করা:
public class MixInExample {
    public static void main(String[] args) throws Exception {
        ObjectMapper mapper = new ObjectMapper();
        mapper.addMixIn(Shape.class, ShapeMixIn.class); // Mix-in যোগ করা

        Shape shape = new Circle();
        shape.color = "Red";
        ((Circle) shape).radius = 5.5;

        String json = mapper.writeValueAsString(shape);
        System.out.println("Serialized JSON:\n" + json);

        Shape deserializedShape = mapper.readValue(json, Shape.class);
        System.out.println("Deserialized Shape Type: " + deserializedShape.getClass().getSimpleName());
    }
}
Output:
{
  "type": "circle",
  "color": "Red",
  "radius": 5.5
}

৩. Custom Serializer এবং Deserializer ব্যবহার করা

Custom Serializer:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;

public class VehicleSerializer extends StdSerializer<Vehicle> {

    public VehicleSerializer() {
        super(Vehicle.class);
    }

    @Override
    public void serialize(Vehicle value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        gen.writeStartObject();
        gen.writeStringField("type", value.getClass().getSimpleName().toLowerCase());
        gen.writeStringField("brand", value.brand);

        if (value instanceof Car) {
            gen.writeNumberField("seatingCapacity", ((Car) value).seatingCapacity);
        } else if (value instanceof Truck) {
            gen.writeNumberField("payloadCapacity", ((Truck) value).payloadCapacity);
        }

        gen.writeEndObject();
    }
}
Custom Deserializer:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;

import java.io.IOException;

public class VehicleDeserializer extends StdDeserializer<Vehicle> {

    public VehicleDeserializer() {
        super(Vehicle.class);
    }

    @Override
    public Vehicle deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        JsonNode node = p.getCodec().readTree(p);
        String type = node.get("type").asText();

        Vehicle vehicle;
        if ("car".equals(type)) {
            vehicle = new Car();
            ((Car) vehicle).seatingCapacity = node.get("seatingCapacity").asInt();
        } else if ("truck".equals(type)) {
            vehicle = new Truck();
            ((Truck) vehicle).payloadCapacity = node.get("payloadCapacity").asDouble();
        } else {
            throw new IllegalArgumentException("Unknown type: " + type);
        }

        vehicle.brand = node.get("brand").asText();
        return vehicle;
    }
}
Module Configuration:
public class CustomSerializerExample {
    public static void main(String[] args) throws Exception {
        SimpleModule module = new SimpleModule();
        module.addSerializer(Vehicle.class, new VehicleSerializer());
        module.addDeserializer(Vehicle.class, new VehicleDeserializer());

        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(module);

        Vehicle car = new Car();
        car.brand = "Honda";
        ((Car) car).seatingCapacity = 4;

        String json = mapper.writeValueAsString(car);
        System.out.println("Custom Serialized JSON:\n" + json);

        Vehicle deserializedVehicle = mapper.readValue(json, Vehicle.class);
        System.out.println("Custom Deserialized Vehicle: " + deserializedVehicle.brand);
    }
}

উপকারিতা

  1. Abstract Class এবং Interface হ্যান্ডল করা সহজতর।
  2. Runtime Polymorphism সাপোর্ট।
  3. Custom Serialization/Deserialization এর মাধ্যমে নির্দিষ্ট নিয়ম প্রয়োগ।

Jackson-এর এই ফিচারগুলো ব্যবহার করে আপনি Abstract Classes এবং Interfaces নিয়ে সহজে কাজ করতে পারবেন।

Content added By
Promotion
NEW SATT AI এখন আপনাকে সাহায্য করতে পারে।

Are you sure to start over?

Loading...